#include "plt_drv.h"

#ifdef LED_DEBUGB
#define dbg_print(fmt, ...)			\
	do{								\
		printk("LED: ");			\
		printk(fmt, ##__VA_ARGS__);	\
		printk("\r\n");				\
	}while(0)
#else
#define dbg_print(fmt, ...) 
#endif

struct led_config{
	//时钟控制位偏移
	uint32_t rcc_pos;
	//引脚号
	uint32_t pin_num;
};

struct led_device{
	//寄存器虚拟地址
	void __iomem *MPU_AHB4_PERIPH_RCC_PI;
	void __iomem *GPIOI_MODER_PI;
	void __iomem *GPIOI_OTYPER_PI;
	void __iomem *GPIOI_OSPEEDR_PI;
	void __iomem *GPIOI_PUPDR_PI;
	void __iomem *GPIOI_BSRR_PI;

	//混杂设备对象
	struct miscdevice misc;
	//LED配置信息
	struct led_config *led_config;
	//led状态，0灭，1亮
	int led_state;
};

//LED设备对象
struct led_device led_device;

static int led_open(struct inode *inode, struct file *file);
static int led_release(struct inode *inode, struct file *file);
static ssize_t led_read(struct file *file, char __user *buf, size_t len, loff_t *pos);
static ssize_t led_write(struct file *file, const char __user *buf, size_t len, loff_t *pos);

static struct file_operations led_ops = {
	.owner = THIS_MODULE,
	.open = led_open,
	.release = led_release,
	.read = led_read,
	.write = led_write,
};

static void led_switch(struct led_device *led, uint8_t state)
{
	uint32_t val = 0;

	if(state)
	{
		val = readl(led->GPIOI_BSRR_PI);
		val |= (1 << (16+led->led_config->pin_num));	
		writel(val, led->GPIOI_BSRR_PI);
	}
	else
	{
		val = readl(led->GPIOI_BSRR_PI);
		val |= (1 << led->led_config->pin_num);	
		writel(val, led->GPIOI_BSRR_PI);
	}	
}

static int led_open(struct inode *inode, struct file *file)
{	
	file->private_data = &led_device;

	return 0;
}

static int led_release(struct inode *inode, struct file *file)
{
	return 0;
}

static ssize_t led_read(struct file *file, char __user *buf, size_t len, loff_t *pos)
{
	char kernal_data;
	struct led_device *led = file->private_data;

	if(led->led_state == 0)
		kernal_data = '0';
	else if(led->led_state == 1)
		kernal_data = '1';
	else
		return -EINVAL;
	
	//将数据从内核空间拷贝到用户空间
	if(copy_to_user(buf, &kernal_data, 1))
		return -EFAULT;

	return 1;
}

static ssize_t led_write(struct file *file, const char __user *buf, size_t len, loff_t *pos)
{
	char user_data;
	struct led_device *led = file->private_data;
	
	//将数据从用户框架拷贝到内核框架
	if(copy_from_user(&user_data, buf, 1))
		return -EFAULT;

	if(user_data == '0')
	{
		led->led_state = 0;
		led_switch(led, 0);
	}
	else if(user_data == '1')
	{
		led->led_state = 1;
		led_switch(led, 1);
	}
	else
		return -EINVAL;

	return len;
}

//取消I/O内存映射
static void io_unmap(struct led_device *led)
{
	//取消I/O内存映射
	if(led->MPU_AHB4_PERIPH_RCC_PI)
	{
		iounmap(led->MPU_AHB4_PERIPH_RCC_PI);
		led->MPU_AHB4_PERIPH_RCC_PI = NULL;
	}
	if(led->GPIOI_MODER_PI)
	{
		iounmap(led->GPIOI_MODER_PI);
		led->GPIOI_MODER_PI = NULL;
	}
	if(led->GPIOI_OTYPER_PI)
	{
		iounmap(led->GPIOI_OTYPER_PI);
		led->GPIOI_OTYPER_PI = NULL;
	}
	if(led->GPIOI_OSPEEDR_PI)
	{
		iounmap(led->GPIOI_OSPEEDR_PI);
		led->GPIOI_OSPEEDR_PI = NULL;
	}
	if(led->GPIOI_PUPDR_PI)
	{
		iounmap(led->GPIOI_PUPDR_PI);
		led->GPIOI_PUPDR_PI = NULL;
	}
	if(led->GPIOI_BSRR_PI)
	{
		iounmap(led->GPIOI_BSRR_PI);
		led->GPIOI_BSRR_PI = NULL;
	}
}

//进行I/O内存映射
static int io_map(struct platform_device *pdev, struct led_device *led)
{
	struct resource *resource;

	//获取设备资源
	resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if(!resource)
	{
		io_unmap(led);
		return -EIO;
	}
	//进行I/O内存地址映射
	led->MPU_AHB4_PERIPH_RCC_PI = ioremap(resource->start, resource_size(resource));
	if(!led->MPU_AHB4_PERIPH_RCC_PI)
	{
		io_unmap(led);
		return -EIO;
	}

	resource = platform_get_resource(pdev, IORESOURCE_MEM, 1);
	if(!resource)
	{
		io_unmap(led);
		return -EIO;
	}
	led->GPIOI_MODER_PI = ioremap(resource->start, resource_size(resource));
	if(!led->GPIOI_MODER_PI)
	{
		io_unmap(led);
		return -EIO;
	}

	resource = platform_get_resource(pdev, IORESOURCE_MEM, 2);
	if(!resource)
	{
		io_unmap(led);
		return -EIO;
	}
	led->GPIOI_OTYPER_PI = ioremap(resource->start, resource_size(resource));
	if(!led->GPIOI_OTYPER_PI)
	{
		io_unmap(led);
		return -EIO;
	}

	resource = platform_get_resource(pdev, IORESOURCE_MEM, 3);
	if(!resource)
	{
		io_unmap(led);
		return -EIO;
	}
	led->GPIOI_OSPEEDR_PI = ioremap(resource->start, resource_size(resource));
	if(!led->GPIOI_OSPEEDR_PI)
	{
		io_unmap(led);
		return -EIO;
	}

	resource = platform_get_resource(pdev, IORESOURCE_MEM, 4);
	if(!resource)
	{
		io_unmap(led);
		return -EIO;
	}
	led->GPIOI_PUPDR_PI = ioremap(resource->start, resource_size(resource));
	if(!led->GPIOI_PUPDR_PI)
	{
		io_unmap(led);
		return -EIO;
	}

	resource = platform_get_resource(pdev, IORESOURCE_MEM, 5);
	if(!resource)
	{
		io_unmap(led);
		return -EIO;
	}
	led->GPIOI_BSRR_PI = ioremap(resource->start, resource_size(resource));
	if(!led->GPIOI_BSRR_PI)
	{
		io_unmap(led);
		return -EIO;
	}

	return 0;
}

static void gpio_init(struct led_device *led)
{
	uint32_t val;
	
	/* 使能PI时钟 */
	val = readl(led->MPU_AHB4_PERIPH_RCC_PI);
	val |= (0X1 << led->led_config->rcc_pos);
	writel(val, led->MPU_AHB4_PERIPH_RCC_PI);
	/* 设置PI0通用的输出模式。*/
	val = readl(led->GPIOI_MODER_PI);
	val &= ~(0X3 << (2*led->led_config->pin_num));
	val |= (0X1 << (2*led->led_config->pin_num));
	writel(val, led->GPIOI_MODER_PI);
	/* 设置PI0为推挽模式。*/
	val = readl(led->GPIOI_OTYPER_PI);
	val &= ~(0X1 << led->led_config->pin_num);
	writel(val, led->GPIOI_OTYPER_PI);
	/* 设置PI0为高速。*/
	val = readl(led->GPIOI_OSPEEDR_PI);
	val &= ~(0X3 << (2*led->led_config->pin_num));
	val |= (0x2 << (2*led->led_config->pin_num));
	writel(val, led->GPIOI_OSPEEDR_PI);
	/* 设置PI0为上拉。*/
	val = readl(led->GPIOI_PUPDR_PI);
	val &= ~(0X3 << (2*led->led_config->pin_num));
	val |= (0x1 << (2*led->led_config->pin_num));
	writel(val, led->GPIOI_PUPDR_PI);
	/* 默认开启LED */
	led_switch(led, 1);
	led->led_state = 1;
}

static void gpio_deinit(struct led_device *led)
{
	uint32_t val;

	/* 关闭LED */
	led_switch(led, 0);
	led->led_state = 0;
	/* 设置PI0为悬空。*/
	val = readl(led->GPIOI_PUPDR_PI);
	val &= ~(0X3 << (2*led->led_config->pin_num));
	writel(val, led->GPIOI_PUPDR_PI);
	/* 设置PI0通用的输入模式。*/
	val = readl(led->GPIOI_MODER_PI);
	val &= ~(0X3 << (2*led->led_config->pin_num));
	writel(val, led->GPIOI_MODER_PI);
}

static int register_led(struct platform_device *pdev, struct led_device *led)
{
	//注册混杂设备
	led->misc.minor = MISC_DYNAMIC_MINOR;
	led->misc.name = pdev->name;
	led->misc.fops = &led_ops;
	return misc_register(&(led->misc));
}

static void unregister_led(struct led_device *led)
{
	//注销混杂设备
	misc_deregister(&led->misc);
}

//设备和驱动匹配成功执行
static int pled_probe(struct platform_device *pdev)
{
	int result;
	struct led_device *led;

	printk("led_init\r\n");

	led = &led_device;
	//复位LED对象
	memset(led, 0, sizeof(struct led_device));

	//获取设备私有数据
	led->led_config = pdev->dev.platform_data;

	//IO内存映射
	result = io_map(pdev, led);
	if(result != 0)
	{
		dbg_print("map io mem failed\r\n");
		return result;
	}

	//注册LED设备
	result = register_led(pdev, led);
	if(result != 0)
	{
		dbg_print("register led failed\r\n");
		io_unmap(led);
		return result;
	}

	//初始化LED
	gpio_init(led);

	return 0;
}

//设备或驱动卸载时执行
static int pled_remove(struct platform_device *pdev)
{
	struct led_device *led;

	printk("led_exit\r\n");

	led = &led_device;
	//反初始化GPIO
	gpio_deinit(led);
	//注销led设备
	unregister_led(led);
	//取消IO内存映射
	io_unmap(led);

	return 0;
}

//系统关机前执行
static void pled_shutdown(struct platform_device *pdev)
{
	printk("%s resume\r\n", pdev->name);
}

//系统进入睡眠状态之前执行
static int pled_suspend(struct platform_device *pdev, pm_message_t state)
{
	printk("%s shutdown\r\n", pdev->name);
	return 0;
}

//系统从睡眠状态中唤醒系统后执行
static int pled_resume(struct platform_device *pdev)
{
	printk("%s resume\r\n", pdev->name);
	return 0;
}

//平台驱动
struct platform_driver pled_drv = {
	.driver = {
		.name = "myled",				//平台驱动名称
		.owner = THIS_MODULE,
		.pm = NULL,
	},
	.probe = pled_probe,				//设备和驱动匹配成功执行
	.remove = pled_remove,				//设备或驱动卸载时执行
	.shutdown = pled_shutdown,			//系统关机前执行
	.suspend = pled_suspend,			//系统休眠前执行
	.resume = pled_resume,				//系统唤醒后执行
};

#if 0
static int __init plt_drv_init(void)
{
	int err = 0;

	//注册平台驱动
	err = platform_driver_register(&pled_drv);
	return err;
}

static void __exit plt_drv_exit(void)
{
	//注销平台驱动
	platform_driver_unregister(&pled_drv);
}

module_init(plt_drv_init);
module_exit(plt_drv_exit);
#else
//利用宏展开模块的加载和卸载函数
module_platform_driver(pled_drv);
#endif

MODULE_LICENSE("GPL");
MODULE_AUTHOR("LF");
MODULE_DESCRIPTION("pled_dev");
